/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core.io;

import java.io.StringWriter;
import java.io.Writer;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.Validate;
import org.jfab.core.collection.CGAnnotationList;
import org.jfab.core.collection.CGConstructorList;
import org.jfab.core.collection.CGEnumItemList;
import org.jfab.core.collection.CGFieldList;
import org.jfab.core.collection.CGInitializationBlockList;
import org.jfab.core.collection.CGMethodList;
import org.jfab.core.collection.CommentList;
import org.jfab.core.collection.ModifierList;
import org.jfab.core.factory.StringFactory;
import org.jfab.core.factory.TypeReferenceSupport;
import org.jfab.core.formatter.Formatter;
import org.jfab.core.model.CGConstructor;
import org.jfab.core.model.CGEnum;
import org.jfab.core.model.CGEnumItem;
import org.jfab.core.model.CGField;
import org.jfab.core.model.CGMethod;

/**
 * Provides an enum writer.
 */
public final class CGEnumWriter extends CGConcreteUnitWriter<CGEnum>
{
    /**
     * Construct this object with the given parameters.
     * 
     * @param typeReferenceSupport Type reference support.
     * @param stringFactory String factory.
     * @param basicCodeGenerator Basic code generator.
     * @param fieldWriter Field writer.
     * @param formatter Code formatter.
     * 
     * @param initializationBlockWriter Initialization block writer.
     * @param constructorWriter Constructor writer.
     * @param methodWriter Method writer.
     */
    public CGEnumWriter(final TypeReferenceSupport typeReferenceSupport,
            final StringFactory stringFactory,
            final BasicCodeGenerator basicCodeGenerator,
            final CGWriter<CGField> fieldWriter, final Formatter formatter,
            final CGInitializationBlockWriter initializationBlockWriter,
            final CGWriter<CGConstructor> constructorWriter,
            final CGWriter<CGMethod> methodWriter)
    {
        super(typeReferenceSupport, stringFactory, basicCodeGenerator,
                fieldWriter, formatter, initializationBlockWriter,
                constructorWriter, methodWriter);
    }

    /**
     * Write an enum.
     * 
     * @param writer Writer.
     * @param cgEnum Enum.
     */
    @Override
    public final void write(final Writer writer, final CGEnum cgEnum)
    {
        Validate.notNull(cgEnum, "cgEnum is null");

        final TypeReferenceSupport typeReferenceSupport = getTypeReferenceSupport();
        typeReferenceSupport.clear();
        typeReferenceSupport.determineReferencedTypes(cgEnum);

        final String packageName = cgEnum.getPackageName();
        final CommentList commentLines = new CommentList(
                cgEnum.getCommentLines());
        final CGAnnotationList annotations = new CGAnnotationList(
                cgEnum.getAnnotations());
        final ModifierList modifiers = new ModifierList(cgEnum.getModifiers());
        final String enumName = cgEnum.getName();
        final List<String> typeParameters = cgEnum.getTypeParameters();
        final Set<String> importClasses = typeReferenceSupport
                .getImportTypeNames(cgEnum);

        final StringWriter stringWriter = new StringWriter();

        writePackageDeclaration(stringWriter, packageName);
        getBasicCodeGenerator().writeBlankLine(stringWriter);
        writeImports(stringWriter, importClasses);
        getBasicCodeGenerator().writeBlankLine(stringWriter);
        getBasicCodeGenerator().writeMultilineJavadocComment(stringWriter,
                commentLines);
        writeEnumDeclaration(stringWriter, annotations, modifiers, enumName,
                typeParameters);
        getBasicCodeGenerator().writeOpenBrace(stringWriter);

        writeEnumItems(stringWriter, new CGEnumItemList(cgEnum.getEnumItems()));
        writeFields(stringWriter, new CGFieldList(cgEnum.getFields()));
        writeInitializationBlocks(stringWriter, new CGInitializationBlockList(
                cgEnum.getInitializationBlocks()));
        writeConstructors(stringWriter,
                new CGConstructorList(cgEnum.getConstructors()));
        writeMethods(stringWriter, new CGMethodList(cgEnum.getMethods()));

        getBasicCodeGenerator().writeCloseBrace(stringWriter);

        format(writer, stringWriter);
    }

    /**
     * @param writer Writer.
     * @param annotations Annotations.
     * @param modifiers Modifiers (@see java.lang.reflect.Modifier)
     * @param enumName Enum name.
     * @param typeParameters Type parameters.
     */
    private void writeEnumDeclaration(final Writer writer,
            final CGAnnotationList annotations, final ModifierList modifiers,
            final String enumName, final List<String> typeParameters)
    {
        Validate.notNull(writer, "writer is null");
        Validate.notEmpty(enumName, "className is empty");

        final String annotationsString = getStringFactory()
                .createAnnotationsString(annotations);
        final String modifiersString = getStringFactory()
                .createModifiersString(modifiers);
        final String typeParametersString = getStringFactory()
                .createTypeParametersString(typeParameters);

        getBasicCodeGenerator().writeLine(
                writer,
                annotationsString + modifiersString + "enum " + enumName
                        + typeParametersString);
    }

    /**
     * Write enum items.
     * 
     * @param writer Writer.
     * @param enumItems Enum items.
     */
    private void writeEnumItems(final Writer writer,
            final CGEnumItemList enumItems)
    {
        if (CollectionUtils.isNotEmpty(enumItems))
        {
            final int size = enumItems.size();

            for (int i = 0; i < size; i++)
            {
                final CGEnumItem cgEnumItem = enumItems.get(i);

                final CommentList commentLines = new CommentList(
                        cgEnumItem.getCommentLines());

                if (CollectionUtils.isNotEmpty(commentLines))
                {
                    if (commentLines.size() == 1)
                    {
                        getBasicCodeGenerator().writeSingleLineJavadocComment(
                                writer, cgEnumItem.getCommentLines().get(0));
                    }
                    else
                    {
                        getBasicCodeGenerator().writeMultilineJavadocComment(
                                writer,
                                new CommentList(cgEnumItem.getCommentLines()));
                    }
                }

                final String params = cgEnumItem.getParameters();
                final String parametersString = (params != null)
                        && (params.length() > 0) ? "(" + params + ")" : "";
                final String line = cgEnumItem.getName() + parametersString
                        + (i < (size - 1) ? "," : ";");
                getBasicCodeGenerator().writeLine(writer, line);
            }
        }
    }
}
